package com.ztjava.pay.tool;

import java.io.StringReader;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.ztjava.core.Constant;
import com.ztjava.core.Jsub;
import com.ztjava.core.tool.ViewTool;
import com.ztjava.core.utiliy.StringUtil;
import com.ztjava.pay.model.PayOrder;

public class WeixinPayTool {
	private static Logger logger = Logger.getLogger(WeixinPayTool.class);
	private static final String PAY_WEIXIN_APPID = "PAY_WEIXIN_APPID";
	private static final String PAY_WEIXIN_MCHID = "PAY_WEIXIN_MCHID";
	private static final String PAY_WEIXIN_APIKEY = "PAY_WEIXIN_APIKEY";
	
	public static Ret getPrepayId(PayOrder payOrder,String domain,String ip,
			String pre_path,String openid){		
		int total_fee = payOrder.getBigDecimal("money").multiply(new BigDecimal("100")).intValue();
		SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
		parameters.put("appid", ViewTool.param(PAY_WEIXIN_APPID));
		parameters.put("mch_id", ViewTool.param(PAY_WEIXIN_MCHID));
		parameters.put("nonce_str", StringUtil.getRandomString(32));
		parameters.put("body", payOrder.getStr("title"));
		parameters.put("out_trade_no",payOrder.getStr("num"));
		parameters.put("total_fee", total_fee);
		parameters.put("spbill_create_ip", ip);
		
		logger.warn("支付参数："+domain +Jsub.path+pre_path	+"/pay/weixin/notifyResult");
		parameters.put("notify_url",domain +Jsub.path+pre_path+"/pay/weixin/notifyResult");
		parameters.put("trade_type", "JSAPI");
		parameters.put("openid", openid);
		parameters.put("sign", WeixinPayTool.createSign(parameters,ViewTool.param(PAY_WEIXIN_APIKEY)));
		String xml = WeixinPayTool.createXml(parameters);
		logger.warn(xml);
		String result = "";
		try {
			result = HttpKit.post("https://api.mch.weixin.qq.com/pay/unifiedorder", xml.toString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "网络连接错误！");
		}
		Document document = null;
		try{
			document = DocumentHelper.parseText(result);
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "unifiedorder结果xml为空或解析错误！");
		}
		Element root = document.getRootElement();
		Element return_code = root.element("return_code");
		if(return_code.getText().equals("FAIL")) {
			Element return_msg = root.element("return_msg");
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, return_msg.getText());
			
		}
		Element result_code = root.element("result_code");
		if(result_code.getText().equals("FAIL")) {
			Element err_code_des = root.element("err_code_des");
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, err_code_des.getText());
		}
		return Ret.create(Constant.RESULT, Constant.SUCCESS).set("prepay_id", root.element("prepay_id").getText());
	}
	
	public static Ret getParams(PayOrder payOrder,String domain,String ip,
			String pre_path,String openid){
		Ret ret = getPrepayId(payOrder, domain, ip, pre_path, openid);
		if(ret.getStr(Constant.RESULT).equals(Constant.ERROR)){
			return ret;
		}
		SortedMap<Object,Object> payParams = new TreeMap<Object,Object>();
		payParams.put("appId", ViewTool.param(PAY_WEIXIN_APPID));
		payParams.put("timeStamp", System.currentTimeMillis()/1000L);
		payParams.put("nonceStr", StringUtil.getRandomString(32));
		payParams.put("package","prepay_id="+ret.getStr("prepay_id"));
		payParams.put("signType","MD5");
		payParams.put("paySign",WeixinPayTool.createSign(payParams,ViewTool.param(PAY_WEIXIN_APIKEY)));
		logger.warn(WeixinPayTool.createJson(payParams));
		return Ret.create(Constant.RESULT,Constant.SUCCESS).set("params", WeixinPayTool.createJson(payParams));
		
	}
	/***
	 * 获取AuthAccessToken
	 * @param code
	 * @return
	 */
	public static Ret getAuthAccessToken(String code,String appid,String appsecret){
		String result = "";
		Ret ret = new Ret();
		try{
			Map<String,String> queryParas = new HashMap<String,String>();
			queryParas.put("appid", appid);
			queryParas.put("secret", appsecret);
			queryParas.put("code", code);
			queryParas.put("grant_type", "authorization_code");
			result = HttpKit.get("https://api.weixin.qq.com/sns/oauth2/access_token", queryParas);
		}catch (Exception e){
			e.printStackTrace();
		}
		if(!result.equals("")){
			JSONObject json = JSONObject.parseObject(result);
			if(json.get("errcode")==null||json.getInteger("errcode")==0){
				ret.set(Constant.RESULT, Constant.SUCCESS);
				ret.set("openid", json.getString("openid"));
				ret.set("access_token", json.getString("access_token"));
				ret.set("refresh_token", json.getString("refresh_token"));
				return ret;
			}else {
				return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
			}
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "获取失败");
	}
	@SuppressWarnings("rawtypes")
	public static String createSign(SortedMap<Object,Object> parameters,String apiKey){
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
        while(it.hasNext()) {  
            Map.Entry entry = (Map.Entry)it.next();  
            String k = (String)entry.getKey();  
            Object v = entry.getValue();  
            if(null != v && !"".equals(v) && !"sign".equals(k) &&!"key".equals(k)) {  
                sb.append(k + "=" + v + "&");  
            }
        }  
        sb.append("key=" + apiKey); 
		String sign = HashKit.md5(sb.toString()).toUpperCase();
		return sign;  
	}
	public static String createXml(SortedMap<Object,Object> parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		for(Object key:parameters.keySet()){
			Object v = parameters.get(key); 
			 if(null != v && !"".equals(v)) {  
				 sb.append("<"+key.toString()+"><![CDATA["+v.toString()+"]]></"+key.toString()+">");  
	         }  
		}
		sb.append("</xml>");
		return sb.toString();  
	}
	
	public static String createJson(SortedMap<Object,Object> parameters){
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		for(Object key:parameters.keySet()){
			Object v = parameters.get(key); 
			 if(null != v && !"".equals(v)) {  
				 sb.append("\""+key.toString()+"\":\""+v.toString()+"\",");
			 }
		}
		return sb.substring(0, sb.length()-1).toString()+"}";  
	}
	


    private static String getEncoding(String text) {
        String result = null;

        String xml = text.trim();

        if (xml.startsWith("<?xml")) {
            int end = xml.indexOf("?>");
            String sub = xml.substring(0, end);
            StringTokenizer tokens = new StringTokenizer(sub, " =\"\'");

            while (tokens.hasMoreTokens()) {
                String token = tokens.nextToken();

                if ("encoding".equals(token)) {
                    if (tokens.hasMoreTokens()) {
                        result = tokens.nextToken();
                    }

                    break;
                }
            }
        }

        return result;
    }
	public static Document safeParseXml(String text) throws DocumentException {
        SAXReader reader = new SAXReader();
        Document result = null;
        try{
        	reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
            String encoding = getEncoding(text);

            InputSource source = new InputSource(new StringReader(text));
            source.setEncoding(encoding);
            result = reader.read(source);
            if (result.getXMLEncoding() == null) {
                result.setXMLEncoding(encoding);
            }
        }catch (SAXException e) {
        	e.printStackTrace();
        	logger.warn("支付模块发现XXE实体注入");
    	}
        return result;
	}
}
